#ifndef _PROJECT1_SNIPPETSFORJSON_
#define _PROJECT1_SNIPPETSFORJSON_

#include "BVP/DirectPossionSolver.hpp"
#include "Functions/MyFunctionTable.H"
#include <jsoncpp/json/json.h>

void JsonTranslator(std::string infile_string);

template <class VecExpr, class ScalerExpr>
std::array<Real, 3>
testExample(std::string description,
            const rVec &center,
            const Real radius,
            const ScalerExpr &negDDexpr,
            const VecExpr &Dexpr,
            const ScalerExpr &expr,
            const char *aBcTypes,
            int N,
            Real atol,
            const bool outputPlotData,
            const bool printConvergence,
            const int ordIntpDiriIrBdr,
            const int ordIntpNeumIrBdr);

//=======================================================================================
/**
 * @brief  读取并执行Json文件中的参数.
 * @param  infile_string    Json name.
 */
void JsonTranslator(std::string infile_string)
{
  std::ifstream infile(infile_string);
  assert(infile.good());
  Json::Reader read;
  Json::Value root;
  if (!read.parse(infile, root))
  {
    std::cout << "Error when reading json.\n";
    return;
  }
  std::string description = root["description"].asCString();
  std::vector<int> possibleN;
  rVec center;
  Real radius;
  for (auto &i : root["recGrid"]["possibleN"])
    possibleN.push_back(i.asInt());
  const Real tolerance = root["recGrid"]["tolerance"].asDouble();
  const bool IsRegular = root["embeddedCircle"].empty();
  if (IsRegular)
  {
    center = {0.4, 0.5};
    radius = 0;
  }
  else
  {
    center = {root["embeddedCircle"]["center"][0].asDouble(), root["embeddedCircle"]["center"][1].asDouble()};
    radius = root["embeddedCircle"]["radius"].asDouble();
  }
  std::string aBctypes_str = root["solver"]["boundaryCondition"].asCString();
  const char *aBcTypes = aBctypes_str.c_str();
  const int ordIntpDiriIrBdr = root["solver"]["ordIntpDiriIrBdr"].asInt();
  const int ordIntpNeumIrBdr = root["solver"]["ordIntpNeumIrBdr"].asInt();
  std::string nFuncs[3];
  nFuncs[0] = root["solver"]["phi"]["description"].asCString();
  nFuncs[1] = root["solver"]["phiGrad"]["description"].asCString();
  nFuncs[2] = root["solver"]["negphiLap"]["description"].asCString();
  const bool outputPlotData = root["output"]["plotData"].asBool();
  const bool printConvergence = root["output"]["printConvergence"].asBool();

  std::vector<std::array<Real, 3>> ErrNorms;
  ErrNorms.resize(possibleN.size());
  for (int i = 0; i < possibleN.size(); i++)
    ErrNorms[i] = testExample(description,
                              center,
                              radius,
                              MyRealFuncMap[nFuncs[2]],
                              MyVecFuncMap[nFuncs[1]],
                              MyRealFuncMap[nFuncs[0]],
                              aBcTypes,
                              possibleN[i],
                              tolerance,
                              outputPlotData,
                              printConvergence,
                              ordIntpDiriIrBdr,
                              ordIntpNeumIrBdr);
  if(printConvergence)
  {
  std::cout<<"Convergence Table for "<<description<<'\n';
  printConvergenceTable(possibleN,ErrNorms);
  std::cout<<'\n';
  }
}

//-----------------------------------------------------------------------------------
/**
 * @brief 根据Json文件传递的参数来测试算例,输出结果.
 * @tparam VecExpr   映射到二维向量的表达式       
 * @tparam ScalerExpr 映射到标量的表达式
 * @param  description      算例名称
 * @param  center           圆心坐标
 * @param  radius           圆半径
 * @param  negDDexpr        负拉普拉斯算子作用于函数
 * @param  Dexpr            梯度算子作用于函数
 * @param  expr             函数
 * @param  aBcTypes         边界条件类型
 * @param  N                单边的网格数
 * @param  atol             全局不确定性控制因子
 * @param  outputPlotData   是否将数据打印到文件
 * @param  printConvergence 是否生成收敛阶数表
 * @param  ordIntpDiriIrBdr 不规则Diri边界的离散精度
 * @param  ordIntpNeumIrBdr 不规则Neum边界的离散精度
 * @return std::array<Real, 3> 三种误差范数，分别为 L^\infty, L^1 , L^2
 */
template <class VecExpr, class ScalerExpr>
std::array<Real, 3> testExample(std::string description,
  const rVec &center,
                                const Real radius,
                                const ScalerExpr &negDDexpr,
                                const VecExpr &Dexpr,
                                const ScalerExpr &expr,
                                const char *aBcTypes,
                                int N,
                                Real atol,
                                const bool outputPlotData,
                                const bool printConvergence,
                                const int ordIntpDiriIrBdr,
                                const int ordIntpNeumIrBdr)
{
  std::cout<<"Solving << "<<description<<" >>, N = "<<N<<'\n';
  char bcTypes[5];
  strncpy(bcTypes, aBcTypes, 5);
  RectDomain<Dim> pGrid(Box<Dim>(0, N), 1.0 / N);
  ExcludedCircle<2> rce1(pGrid, (OrientedCircle){center, radius}, atol);
  DirectPossionSolver<2> DPS1(rce1, bcTypes,ordIntpDiriIrBdr,ordIntpNeumIrBdr);
  Tensor<Real, 1> sol1;
  sol1 = DPS1.computeSolution(negDDexpr, Dexpr, expr);
  if(outputPlotData)
  {
    DPS1.plotIn(sol1,"../output/"+description+"_N_"+std::to_string(N)+".m",expr);
    std::cout<<"Plot generated.\n";
  }
  if (printConvergence)
    return (std::array<Real, 3>){DPS1.computeErrNorm(sol1, expr, 0), DPS1.computeErrNorm(sol1, expr, 1), DPS1.computeErrNorm(sol1, expr, 2)};
  else
    return (std::array<Real, 3>){0, 0, 0};
}


#endif // !_PROJECT1_SNIPPETSFORJSON_